perm filename IO.PAL[2,VDS] blob sn#194115 filedate 1975-12-29 generic text, type C, neo UTF8
COMMENT āŠ—   VALID 00018 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00003 00002	IO - TELETYPE IO AND STRING MANIPULATION ROUTINES
C00006 00003	"INSTR" - VT05 INPUT ROUTINE 
C00009 00004	"HOLD" - VT05 ROUTINE TO TEMPORARILY SUSPEND PRINTING
C00010 00005	"RELSCN"- STRING TO FLOATING POINT NUMBER ROUTINE
C00013 00006		   [CONTINUATION OF "RELSCN"]
C00016 00007		   [CONTINUATION OF "RELSCN"]
C00019 00008	"INTSCN"- STRING TO INTEGER NUMBER ROUTINE
C00021 00009	"CLRCMA"- ROUTINE TO CLEAR COMMA BREAK CHARACTER FROM STRING 
C00022 00010	"IDENT" - DECODES STRING COMMAND INTO FUNCTION NUMBER
C00027 00011	"FORMAT"&"RSTFOR" - ROUTINES TO SET AND RESTORE OUTPUT FORMAT 
C00029 00012	"CVF"   - FLOATING POINT NUMBER TO "F" FORMAT STRING ROUTINE 
C00032 00013	"CVE"   - FLOATING POINT NUMBER TO "E" FORMAT STRING ROUTINE 
C00035 00014		   [CONTINUATION OF "CVE"]
C00037 00015	"CVG"   - FLOATING POINT NUMBER TO "E" OR "F" FORMAT STRING  
C00039 00016	"PRTF"  - PRINTING ROUTINE USED BY "CVF", "CVE", & "CVG"
C00042 00017	"CVI"&"CVO"   - INTEGER NUMBER TO ASC STRING 
C00045 00018	LOCAL STORAGE AREA
C00049 ENDMK
CāŠ—;
;IO - TELETYPE IO AND STRING MANIPULATION ROUTINES

.TITLE  IO 

;"OUTSTR" IS A MACRO USED FOR PRINTING OUT A STRING ON THE TELETYPE.  A
;SAMPLE INSTRUCTION USING THIS MACRO FOLLOWS:
;
;		OUTSTR  COMMENT
;
;WHERE COMMENT IS THE STARTING ADDRESS OF A ASCII STRING THAT IS TO BE
;WRITTEN.  THE STRING TERMINATOR MUST BE A NULL CHARACTER ( 0 ).  NO
;REGISTERS ARE AFFECTED BY THIS MACRO CALL.

       .MACRO OUTSTR B		;Type string starting at B.
	MOV 	SG,-(SP)	;Save R5.  Who knows what was happening in it?
	MOV 	#B,SG		;Load up the string to be output
	JSR 	PC,TYPSTR	;Call the string output utility routine.
	MOV 	(SP)+,SG	;Restore SG.
       .ENDM


;"CRLF" IS A SUBROUTINE FOR TYPING OUT ONE CARRIAGE RETURN AND LINE FEED 
;ON THE TELETYPE.

CRLF:	MOV	#CRLFX,SG	
	JSR	PC,TYPSTR
	RTS	PC

CRLFX:  .BYTE	15,12,0,0

;"TYPSTR" OUTPUTS A STRING, ENDING WITH A ZERO CHARACTER.  A POINTER TO
;THE START OF THE STRING MUST BE LOADED INTO R5.  CALLED USING THE PC.

TYPSTR:	MOV 	R0,-(SP)	
	MOVB 	(SG)+,R0	;R0 ← first byte of string
TSLOOP:	JSR 	PC,TYPCHR	;Type this one character
	MOVB 	(SG)+,R0	;R0 ← Next byte of string
	BNE 	TSLOOP		;If more to come, repeat.
	MOV	(SP)+,R0
	RTS 	PC		;Done
TYPCHR:	TSTB 	KBOS		;Is TTY available?
	BPL 	TYPCHR		;No.  Busy wait for it.
	MOVB 	R0,KBOR		;Yes.  Output a byte to it.
	CMP 	#12,R0		;Was it a line feed?
	BNE 	TYPRET		;If not that code, then done.
	CLR 	R0		;Otherwise, output 3 nulls.
	JSR 	PC,TYPCHR	;
	JSR 	PC,TYPCHR	;
	JSR 	PC,TYPCHR	;
TYPRET:	RTS 	PC		;Return.
;"INSTR" - VT05 INPUT ROUTINE 

;STRING BYTE POINTER MUST BE  IN SG.  A CARRIAGE RETURN  IS ASSUMED TO
;BE  THE  ACTIVATION CHARACTER.  A  RUB OUT  IS  A  DELETING BACKSPACE
;CHARACTER.  AT  THE COMPLETION OF  THIS ROUTINE  A NULL CHARACTER  IS
;PLACED IN THE INPUT STRING.  SG THEN POINTS TO THE NULL CHARACTER.

;REGISTERS USED:
;
; 	SG PASSES ARGUMENT AND IS MODIFIED

INSTR:	MOV	R0,-(SP)
      	CLR	CCNT		;RESET CHARACTER COUNT
IN2:  	TSTB	KBIS		;TEST IF KEYBOARD READY
	BEQ	.-4		;WAIT TILL IT IS
	MOVB	KBIR,R0		;GET A CHARACTER
	BIC     #177600,R0	;MASK OFF - MAKE IT 7 BITS
	CMP	R0,#177		;COMPARE TO BS CHARACTER
	BNE	IN3		;SKIP IF ITS NOT
	TST	CCNT		;CHECK IF ANY CHARACTERS IN BUFFER
	BEQ	IN2		;FORGET BACK SPACE IF NO CHAR.
	DEC     SG   		;REMOVE LAST CHARACTER IN BUFFER
	DEC	CCNT		;DECREMENT CHARACTER COUNT
	OUTSTR  DBS		;PERFORM A DELETING BACKSPACE
	JMP     IN2
IN3:	CMP	R0,#15		;COMPARE TO CR CHARACTER
	BEQ     IN4   		;CONTINUE READING IF ITS NOT A CR
	CMP	R0,#40		;CHECK IF CHARACTER LEGAL
	BLT	IN2		;IGNOR IF IT IS
    	MOVB	R0,(SG)+	;SAVE THE CHARACTER
    	INC	CCNT		;INCREMENT CHARACTER COUNT
     	TSTB	KBOS		;ECHO THE CHARACTER
	BPL	.-4		;WAIT TILL TTY READY
	MOVB	R0,KBOR		;WRITE THE CHARACTER
	JMP	IN2		;CONTINUE READING
IN4:	MOV	SG,-(SP)	;IF IT IS A CR, TYPE A CR AND LF
      	JSR	PC,CRLF		
	MOV	(SP)+,SG
	MOVB	R0,(SG)+	;PUT A CR IN THE STRING
	MOVB    #0,(SG)		;PUT IN A NULL CHARACTER
	MOV	(SP)+,R0
	RTS	PC		;RETURN
CCNT:	0
DBS:	.BYTE	10,40,10,0
;"HOLD" - VT05 ROUTINE TO TEMPORARILY SUSPEND PRINTING

;IF A CHARACTER HAS BEEN TYPED ON THE VT05 KEYBOARD, THIS ROUTINE GOES
;INTO A BUSY WAIT LOOP UNTIL ANOTHER CHARACTER IS TYPED.  BOTH CHARACTERS
;ARE LOST.  IF NO CHARACTER HAS BEEN TYPED, THIS ROUTINE RETURNS 
;IMMEDIATELY.

;REGISTERS USED:
;
;	NONE

HOLD:	TSTB	KBIS		;TEST IF CHARACTER TYPED
	BEQ	HLDDNE		;RETURN IF NO CHARACTER
	CLRB	KBIR
	TSTB	KBIS		;ELSE WAIT TILL ANOTHER CHARACTER TYPED
	BEQ	.-4
	CLRB	KBIR
HLDDNE:	RTS	PC



;END OF "HOLD"
;"RELSCN"- STRING TO FLOATING POINT NUMBER ROUTINE

;THE FLOATING POINT NUMBER MUST BE OF THE FORM SIII.DDDESXX WHERE S IS
;THE SIGN OF THE NUMBER, III IS THE INTEGER FIELD,  DDD IS THE DECIMAL
;FIELD,  AND SXX  IS THE EXPONENT  AND ITS SIGN.   THE LENGTH OF  EACH
;FIELD IS VARIABLE  BUT ONLY THE FIRST 8 DIGITS  ARE USED IN COMPUTING
;THE F.P.   NUMBER.  EMPTY FIELDS ARE PERMITTED AND ALL LEADING SPACES
;AND ZEROS ARE IGNORED.  THE LOCATION OF THE FIRST  BYTE OF THE STRING
;MUST  BE LOADED INTO  SG BEFORE  CALLING "RELSCN".   AFTER EXECUTION,
;THIS ROUTINE LEAVES THE F.P. NUMBER IN REGISTER AC0 AND SG POINTS  TO
;THE BYTE FOLLOWING THE LAST DIGIT.  THE C BIT IS USED TO INDICATE AN 
;ERROR CONDITION.  IF NO NUMBER WAS FOUND BEFORE ENCOUNTERING A COMMA
;OR NULL CHARACTER, THE C BIT IS SET OTHERWISE THE C BIT IS CLEARED ON
;EXITING THIS ROUTINE.  "RELSCN" IS CALLED USING THE PC.

;REGISTERS USED:
;
;	AC0,SG PASS ARGUMENTS, NO OTHER REGISTERS AFFECTED


;"DIGIT" CHECKS FOR ASC DIGIT AND CONVERTS TO INTEGER IF IT IS

.MACRO DIGIT NOTDIG
	CMP	R0,#60		;COMPARE TO ASC ZERO
	BLT	NOTDIG		;SKIP IF OUT OF RANGE
	CMP	R0,#71		;COMPARE TO ASC 9
	BGT	NOTDIG		;SKIP IF OUT OF RANGE
	BIC	#60,R0		;MASK OUT ASC BASE
.ENDM

;"CKSIGN" CHECKS FOR A - OR + CHARACTER AND SETS SIGN APPROPRIATELY

.MACRO CKSIGN ISSIGN,NTSIGN,SIGN
	CMP	#53,R0		;IGNOR "+" CHARACTER
	BEQ	ISSIGN
	CMP	#55,R0		;CHECK IF ITS A "-" CHAR.
	BNE	NTSIGN		;EXIT IF ITS NOT
	INC	SIGN		;ELSE SET SIGN NON-ZERO
	JMP	ISSIGN
.ENDM

;START OF "RELSCN"

RELSCN:	MOV	R0,-(SP)	;SAVE REGISTERS
	MOV	R1,-(SP)
	MOV	R2,-(SP)
      	MOV	R3,-(SP)	
      	CLR	R2 		;RESET DIGIT COUNT
	MOV	#1,R3		;SET DECIMAL POINT FLAG
	   [CONTINUATION OF "RELSCN"]

	MOV	#-1,R1		;INDICATE NO DIGITS ENCOUNTERED
 	CLRF	AC0		;CLEAR THE NUMBER ACCUM
	CLR	MSIGN		;ASSUME MANTISSA POSITIVE

;PICK UP A CHARACTER AND CHECK FOR SIGN

PICK:	MOVB	(SG)+,R0	;PICK UP A CHARACTER
	TST	R1		;CHECK IF DIGIT ENCOUNTERED
	BEQ	CHKDG		;SKIP IF TRUE
	CKSIGN	PICK,CHKDG,MSIGN	;CHECK FOR + OR - SIGN

;CHECK IF CHARARCTER IS A DIGIT

CHKDG:	DIGIT	CHKDP		;SKIP TO CHKDP IF NOT A DIGIT
	MULF	TEN,AC0		;MULT DIGIT SUM BY 10
	ASH	#2,R0		;MULTIPLY INDEX BY 4
	ADDF	DGLST(R0),AC0	;ADD THE F.P. TO ACCUM
	CLR     R1    		;INDICATE DIGIT ENCOUNTERED
	SUB     #4,R2		;DECREMENT DIGIT COUNT
	JMP	PICK		;GO GET ANOTHER CHARACTER

;CHECK IF THE CHARACTER IS A DECIMAL POINT

CHKDP:	CMP	#56,R0		;COMPARE CHARACTER TO DECIMAL PT
	BNE	RNORM		;SKIP IF NOT D.P.
      	TST	R3		;CHECK IF DECIMAL POINT ALREADY SET
	BEQ	RNORM		;IF RESET THIS MUST BE A THE END OF THE MANT.
	CLR	R2		;START COUNTING FRACTIONAL DIGITS
	CLR	R3		;INDICATE D.P. SET
	CLR	R1		;INDICATE DIGIT ENCOUNTERED
	JMP	PICK		;GO GET ANOTHER CHARACTER

;CORRECT NUMBER FOR POWER OF TEN IF DIGITS FOUND

RNORM:	TST	R1		;CHECK IF DIGITS FOUND
	BNE	CHKEX		;SKIP IF NONE
	TST	R3		;CHECK IF DECIMAL POINT SET
	BNE	CHKEX		;DONT NORMALIZE IF NO D.P.
    	MULF	TENLST(R2),AC0	;CORRECT DECIMAL POINT

;CHECK IF E SIGN ENCOUNTERED

CHKEX:	CMP	#105,R0		;COMPARE TO E CHARACTER
	BNE	CHKDN		;SKIP IF NOT E
	TST	R1   		;CHECK IF NO DIGITS BEFORE E
	BEQ	EXCN
	LDF	TENLST,AC0	;SET AC0=1 IF EXPONENT BUT NO DIGITS
	   [CONTINUATION OF "RELSCN"]

	CLR	R1		;INDICATE DIGITS ENCOUNTERED
EXCN:	CLR	ESIGN		;ASSUME EXPONENT POSITIVE
	CLR	R3		;CLEAR EXPONENT ACCUMULATOR
	MOVB	(SG)+,R0	;GET NEXT CHARACTER
	CKSIGN	PIC2,DIG2,ESIGN	;CHECK FOR SIGN CHARACTER
PIC2:	MOVB	(SG)+,R0	;SIGN INCOUNTERED, GET NEXT CHAR.
DIG2:	DIGIT	NORM		;EXTRACT DIGIT 
	MUL	#10.,R3		;MULT EXPON REG BY 10.
	ADD	R0,R3		;ADD DIGIT TO EXPONENT REG
	JMP	PIC2		;GO GET ANOTHER CHARACTER

NORM:	TST	ESIGN		;CHECK SIGN OF EXPONENT
	BEQ	.+4
	NEG	R3		;COMPLEMENT EXPONENT IF - SIGN
	ASH	#2,R3		;MULT. INDEX BY 4 FOR F.P. NUMBERS
	MULF	TENLST(R3),AC0	;ADJUST EXPONENT OF NUMBER
	JMP	CDONE		;EXIT ROUTINE

;CHECK IF END OF STRING OR COMMA ENCOUNTERED

CHKDN:	TST     R0		;COMPARE CHARACTER TO A NULL CHARACTER
	BEQ	CDONE		;EXIT IF IT IS, THIS IS THE END OF THE STR
	CMP	#54,R0		;COMPARE TO ","
	BEQ	CDONE		;EXIT IF IT IS
	TST	R1		;TEST IF ANY DIGITS YET
	BLT	PICK		;IF NONE, KEEP SCANNING

;NO MORE DIGITS - APPLY CORRECT SIGN TO NUMBER

CDONE:	DEC	SG		;POINT TO BREAK CHARACTER
    	TST	MSIGN		;TEST SIGN OF MANTISSA
	BEQ	.+4
	NEGF	AC0		;COMPLEMENT NUMBER IF SIGN NEGATIVE
       	TST	R1		;TEST IF NO NUMBER ENCOUNTERED
	BEQ	.+4
	SEC			;SET C REGISTER IF NO NUMBER FOUND
       	MOV	(SP)+,R3	;RESTORE REGISTERS
	MOV	(SP)+,R2
     	MOV	(SP)+,R1
     	MOV	(SP)+,R0
	RTS	PC		;RETURN


;END OF "RELSCN"
;"INTSCN"- STRING TO INTEGER NUMBER ROUTINE

;THE INTEGER NUMBER MUST BE OF THE FORM SIII WHERE S IS THE SIGN OF THE
;NUMBER, AND III IS THE INTEGER FIELD.  ALL LEADING SPACES AND  ZEROS
;ARE IGNORED.  THE LOCATION OF THE FIRST BYTE OF THE STRING MUST BE 
;LOADED INTO REGISTER SG BEFORE CALLING "INTSCN".  AFTER EXECUTION,
;THIS ROUTINE LEAVES THE INTEGER NUMBER IN R0 AND SG POINTS TO
;THE BYTE FOLLOWING THE LAST DIGIT.  THE C BIT IS USED TO INDICATE AN 
;ERROR CONDITION.  IF NO NUMBER WAS FOUND BEFORE ENCOUNTERING A COMMA
;OR NULL CHARACTER, THE C BIT IS SET.  ALSO, IF THE INTEGER NUMBER IS
;TOO LARGE, THE C BIT IS SET, OTHERWISE THE C BIT IS CLEARED ON EXITING
;THIS ROUTINE.  "INTSCN" IS CALLED USING THE PC.

;REGISTERS USED:
;
;	R0,SG PASS ARGUMENTS AND ARE ALTERED
;	AC0 IS GARBAGED

INTSCN:	JSR	PC,RELSCN	;CONVERT STRING NUMBER TO FLOATING POINT
	BCC	.+4
	RTS	PC		;EXIT IF NO NUMBER FOUND
	STCFI	AC0,R0		;ELSE CONVERT NUMBER TO INTEGER
	CFCC			;TRANSFER CODITIONAL CODES
	RTS	PC		;RETURN


;END OF "INTSCN"
;"CLRCMA"- ROUTINE TO CLEAR COMMA BREAK CHARACTER FROM STRING 

;"CLRCMA" CAN BE CALLED FOLLOWING "RELSCN" TO ADJUST THE STRING
;POINTER IN SG TO SKIP OVER THE COMMA CHARACTER WHICH IS USED
;TO SEPARATE NUMBERS IN THE SAME INPUT STRING.  SG MUST BE 
;POINTING AT THE INPUT STRING.  NO OTHER REGISTERS ARE EFFECTED.

CLRCMA:	TSTB	(SG)		;CHECK IF AT END OF STRING
	BNE	.+4	
	RTS	PC		;RETURN IF END OF STRING
	CMPB	#54,(SG)+	;COMPARE TO COMMA CHARACTER
	BNE	CLRCMA		;BRANCH IF IT ISN'T
	RTS	PC		


;END OF "CLRCMA"
;"IDENT" - DECODES STRING COMMAND INTO FUNCTION NUMBER

;THIS PROGRAM HASHES THE FIRST WORD IN A GIVEN STRING AND USES THE HASH
;NUMBER AS A INDEX INTO A BYTE TABLE CONTAINING FUNCTION NUMBERS.  THE
;CORRESPONDING FUNCTION NUMBER IS RETRIEVED AND USED AS A INDEX INTO
;A GIVEN TABLE OF STRINGS CONTAINING THE FUNCTION NAMES.  IF A STRING
;MATCH IS OBTAINED, THE FUNCTION NUMBER IS RETURNED IN REGISTER R0.  IF
;THE STRING IS FOUND TO CONTAIN NO CHARACTERS THE C BIT IS SET AND R1
;IS CLEARED.  IF THE STRING CONTAINS CHARACTERS BUT NO MATCH IS FOUND,
;THE C BIT IS SET AND R1 IS SET TO -1.  A CARRIAGE RETURN CHARACTER IS
;ASSUMED TO END THE INPUT STRING.  A SAMPLE CALLING SEQUENCE FOLLOWS:
;
;		MOV	#HASHTB,R0	;POINTER TO FUNCTION/HASH TABLE
;		MOV	#STGLST,R1	;PTR TO TABLE OF PTRS TO STRINGS
;		MOV	#STRING,SG	;PTR TO INPUT STRING
;		JSR	PC,IDENT
;		BCS	ERROR
;
;IF IDENT IS SUCCESSFUL, SG IS LEFT POINTING AT THE BREAK CHARACTER
;FOLLOWING THE COMMAND WORD.

IDENT:	MOV	R2,-(SP)	;SAVE REGISTERS
	MOV	R3,-(SP)
	MOV	R4,-(SP)
	CMPB	#40,(SG)+	;IGNOR ALL LEADING SPACE CHARACTERS
	BEQ	.-4
	TSTB	-(SG)		;POINT TO FIRST NON-SPACE CHARACTER
	MOV	SG,-(SP)	;SAVE STRING POINTER
	CLR	CARCNT		;COUNT NUMBER OF CHAR. HASHED TOGETHER
	CLR	R2		;FORM HASH IN HERE
IDEN1:	CMPB	#15,(SG)	;CHECK IF END OF LINE = CR CHARACTER
	BEQ	IDEN2
	CMPB	#40,(SG)	;CHECK IF END OF WORD = SPACE CHARACTER
	BEQ	IDEN2
	MOVB	(SG)+,R3
	ADD 	R3,R2		;ELSE ADD CHARACTERS TOGETHER
	INC	CARCNT		;INDICATE ONE MORE CHAR. IN WORD
	BR	IDEN1
IDEN2:	TST	CARCNT		;CHECK IF ANY CHARACTERS FOUND
	BNE	IDEN3
	CLR	R1		;INDICATE EMPTY LINE
	BR	IDEN8
IDEN3:	MOV	#1,R3		;NEED THIS IF REHASH REQUIRED
      	BIC	#177740,R2	;USE 5 LSB AS HASH INDEX
	MOV	R2,OHASH	;SAVE ORIGINAL HASH NUMBER
IDEN4:	ADD	R0,R2		;GET ADDR. INTO HASH TABLE
	MOVB	(R2),R2		;GET INDEX INTO FUNCTION TABLE
      	MOV	R2,HASH
	BMI	IDEN7		;ILLEGAL INSTRUCTION IF MINUS
	ASL	R2		;CONVERT TO WORD INDEX
	ADD	R1,R2		;NOW HAVE POINTER TO FUNCTION NAME STG.
	MOV	(R2),R2
       	MOV	(SP),SG		;GET POINTER TO START OF WORD
	MOV	CARCNT,R4	;GET NUMBER OF CHARACTERS IN WORD
IDEN5:	CMPB	(R2)+,(SG)+	;COMPARE STRINGS CHARACTER BY CHARACTER
	BNE	IDEN6		;BRANCH IF NOT THE SAME
	SOB	R4,IDEN5	
	TSTB	(R2)		;THIS SHOULD BE THE END OF THE NAME STG
	BEQ	IDNDNE		;EXIT IF STRINGS EXACTLY ALIKE
IDEN6: 	MUL	#5,R3		;NEED TO DETERMINE A NEW HASH NUMBER OF NAME
	BIC	#177600,R3	
	MOV	R3,R2
	ASH	#-2,R2		;THIS IS INCREMENT TO OLD HASH NUMBER
	BEQ	IDEN7		;IF ZERO, WE LOOKED AT WHOLE TABLE ALREADY
	ADD	OHASH,R2	;ELSE ADD ORIGINAL HASH INDEX
	BIC	#177740,R2
	BR	IDEN4		;GO TRY MATCHING CHARACTERS AGAIN
IDEN7:	MOV	#-1,R1    	;INDICATE ILLEGAL INSTRUCTION
IDEN8:	SEC	         	
IDNDNE:	MOV	HASH,R0		;RETURN FUNCTION NUMBER
	MOV	(SP)+,R4	;LEAVE SG POINTING AT BREAK CHARACTER
	MOV	(SP)+,R4	;RESTORE REGISTERS
	MOV	(SP)+,R3
	MOV	(SP)+,R2
	RTS	PC

OHASH:	0		;ORIGINAL HASH NUMBER
HASH:	0		;CURRENT HASH NUMBER
CARCNT:	0		;NUMBER OF CHARACTERS IN COMMAND WORD

 
;END OF "IDENT"
;"FORMAT"&"RSTFOR" - ROUTINES TO SET AND RESTORE OUTPUT FORMAT 

;THE TOTAL NUMBER OF CHARACTERS TO BE WRITTEN (WIDTH) SHOULD BE
;LOADED INTO R0 AND THE NUMBER OF DECIMAL DIGITS (DIGITS) SHOULD
;BE LOADED INTO R1 BEFORE CALLING THIS ROUTINE.  IN ALL CASES,
;WIDTH SHOULD BE GREATER THAN OR EQUAL TO DIGIT+2.  "FORMAT" IS
;CALLED BY USING THE PC.

;REGISTERS USED:
;
;	R0,R1 PASS ARGUMENTS
;	NO OTHER REGISTERS AFFECTED

FORMAT:	MOV	WIDTH,OLDW	;SAVE THE OLD WIDTH
	MOV	DIG,OLDD	;   AND DIG
       	SUB	#2,R0		;SUBTRACT SPACES FOR SIGN AND . FROM WIDTH
	MOV	R0,WIDTH	;SAVE WIDTH OF I/O STRING - 2
	MOV	R1,DIG		;SAVE THE NUMBER OF DECI. DIGITS
	CMP	R0,R1		;CHECK TO SEE THAT WIDTH.GE.DIGIT+2
	BGE	NFER		;SKIP IF SPACE ALLOWED, ELSE CORRECT
	OUTSTR	FERM		;TYPE OUT ERROR MESSAGE
	MOV	R1,WIDTH	;SET WIDTH=DIG+2
NFER:	RTS	PC		;RETURN

FERM:	.ASCIZ /
FORMATTING ERROR
/
	.EVEN


;"RSTFOR" - ROUTINE TO RESTORE LAST FORMAT

;THE PREVIOUS FORMAT BECOMES THE CURRENT FORMAT.  THE CURRENT
;FORMAT IS LOST FOREVER.  "RSTFOR" IS CALLED IN USING THE PC.

;REGISTERS USED:  NONE

RSTFOR:	MOV	OLDW,WIDTH	;RESTORE WIDTH
	MOV	OLDD,DIG	;RESTORE DIG
	RTS	PC		;RETURN


;END OF "FORMAT" &"RSTFOR"
;"CVF"   - FLOATING POINT NUMBER TO "F" FORMAT STRING ROUTINE 

;"CVF" - THE STRING GENERATED BY THIS ROUTINE IS SIMILAR TO "F" FORMAT
;IN  FORTRAN.  IT  IS ASSUMED  THAT THE NUMBER  TO BE CONVERTED  IS IN
;REGISTER AC0  AND SG  CONTAINS A  POINTER TO  THE FIRST  BYTE OF  THE
;OUTPUT STRING.  THE NUMBER OF  CHARACTERS WRITTEN SHOULD FIRST BE SET
;IN  A CALL  TO "FORMAT", ELSE  THE DEFAULT VALUES  ARE USED.   IF THE
;INTEGER PART  OF  THE  NUMBER EXCEEDS  THE  FORMAT LIMITS  THE  FIRST
;CHARACTER WRITTEN  IS A ">".   AFTER COMPLETION, "CVF"  LEAVES A NULL
;CHARACTER FOLLOWING THE NUMBER STRING.  REGISTER SG IS LEFT  POINTING
;AT THE NULL CHARACTER.

;REGISTERS USED:
;
;	SG,AC0 PASS ARGUMENTS AND ARE ALTERED
;	AC1 IS GARBAGED

CVF:	MOV	R1,-(SP)	;SAVE REGISTER
    	MOV	WIDTH,R1	;GET THE TOTAL NUMBER OF CHAR TO BE WRITTEN
	SUB	DIG,R1		;DETERMINE THE MAG. OF THE M.S. DIGIT
	MOV	R1,PT		;NOW HAVE # OF DIGITS BEFORE DECIMAL POINT
	ASH	#2,R1		;X 4, USE AS INDEX INTO F.P. TABLE
	NEG	R1
	MULF	TENLST(R1),AC0	;NORMALIZE NUMBER TO BETWEEN 0 AND .99999999
	MOV	WIDTH,R1	;TOTAL # OF DIGITS TO R1
	MOV	R2,-(SP)	;SAVE THE REGISTERS
	MOV	R3,-(SP)
	JSR	PC,PRTF		;TYPE OUT THE DIGITS
	MOVB	#0,(SG)		;PUT A NULL CHARACTER AFTER THE STRING
	MOV	(SP)+,R3	;RESTORE THE REGISTERS
	MOV	(SP)+,R2
	MOV	(SP)+,R1
	RTS	PC		;RETURN



;END OF "CVF"
;"CVE"   - FLOATING POINT NUMBER TO "E" FORMAT STRING ROUTINE 

;"CVE" - SAME OPERATION AS "CVF" EXCEPT THAT OUTPUT IN FORTRAN "E" FORMAT

CVE:	MOV	R1,-(SP)
     	MOV	R2,-(SP)	;SAVE THE REGISTERS
	MOV 	R3,-(SP)
        CLR     EXPON		;RESET EXPONENT COUNT
	MOV	#1,PT		;SET COUNT TO PRINT 1 NUMBER BEFORE DECIMAL PT
	MOV	WIDTH,R1	;SET COUNT FOR TOTAL NUMBER OF DIGITS TO BE SENT
	SUB	#4,R1		;ADJUST FOR EXPONENT
	TSTF	AC0		;CHECK IF NUMBER IS ZERO
	CFCC			;TRANSFER CONDITIONAL CODES TO CPU
	BEQ	EPRT		;START PRINTING IF NUMBER IS 0.0
     	STF	AC0,NUM		;GET THE NUMBER TO BE CONVERTED
	DEC	EXPON		;ADJUST EXPONENT FOR PRINTING 1 INT. DIGIT
	MOV	NUM,R2		;LOAD THE EXPONENT AND MSB OF THE NUMBER
	BIC	#100000,R2	;CONVERT TO ABSOLUTE VALUE
	SUB	#150,R2		;ADJUST EXPONENT DOWN
	BGE	.+4
	CLR	R2		;LEAVE IT POSITIVE
	MUL	#233,R2		;USE EXPONENT AND MSB AS INDEX INTO TEN TABLE
	CMP	R2,#76.		;COMPARE TO 1.0@38
	BLE	.+6
	MOV	#76.,R2		;IF LARGER, REPLACE BY 1.0@38
      	SUB	#38.,R2		;SHIFT INDEX INTO RANGE OF -38 TO +38
	ADD	R2,EXPON	;ADJUST EXPONENT COUNT
	ASH	#2,R2		;MULT INDEX BY 4 FOR FLOATING POINT NUMBERS
	NEG	R2
	MULF	TENLST(R2),AC0	;NORMALIZE NUMBER INTO RANGE 0.0 TO 0.9999
	STF	AC0,AC1		;GET ABSOLUTE VALUE OF NUMBER
	ABSF	AC1
	CMPF	TENLST,AC1	;CHECK IF NUMBER LESS THAN 1.0
	CFCC			;TRANSFER CONDITIONAL CODES TO CPU
	BGT	EPRT		;IF ITS BETWEEN 0.0 AND .99999, GO TO PNTF
	MULF	TENTH,AC0	;ELSE MULT. BY 0.1 AND ADJUST EXPONENT
        INC	EXPON
	   [CONTINUATION OF "CVE"]

EPRT:	JSR	PC,PRTF		;GO PRINT MANTISSA
	MOVB	#105,(SG)+	;PUT A "E" CHAR INTO THE STRING
	MOVB	#53,(SG)+	;ASSUME EXPONENT POSITIVE A OUTPUT A "+"
	MOV	EXPON,R3	;TEST SIGN OF EXPONENT
	BGE	XPRT		;SKIP IF POSITIVE
	MOVB	#55,-1(SG)	;REPLACE "+" WITH "-"
	NEG	R3    		;MAKE EXPONENT POSITIVE
XPRT:	CLR	R2		;CLEAR FOR DIVISION
     	DIV	#10.,R2		;SEPARATES TENS AND UNITS DIGIT
	BIS	#60,R2		;CONVERT TO ASC AND PUT IN I/O BUFFER
	MOVB	R2,(SG)+
	BIS	#60,R3
	MOVB	R3,(SG)+
	MOVB	#0,(SG)		;PUT IN A NULL CHARACTER
	MOV	(SP)+,R3	;RESTORE THE REGISTERS
	MOV	(SP)+,R2
	MOV	(SP)+,R1
	RTS	PC		;RETURN



;END OF "CVE"
;"CVG"   - FLOATING POINT NUMBER TO "E" OR "F" FORMAT STRING  

;"CVG" - DETERMINES IF THE NUMBER IN AC0 CAN BE WRITTEN BY "CVF", IF
;IT CAN, THEN CVF IS CALLED, ELSE THE NUMBER IS PRINTED USING "CVE".

CVG:	MOV	R1,-(SP)
    	LDF	AC0,AC1		;COPY THE  NUMBER
	CFCC			;TRANSFER THE CONDITIONAL CODES TO CPU
	ABSF	AC1		;CONVERT NUMBER TO ABSOLUTE VALUES
	BEQ	RUNF		;IF NUMBER = 0.0, EXECUTE CVF
	MOV	DIG,R1		;GET THE NUMBER OF DECIMAL DIGITS TO BE TYPED
	ASH	#2,R1		;MULT BY 4 TO USE A FLOATING POINT INDEX
	MULF	TENLST(R1),AC1	;CHECK IF NUMBER SMALLER THAN 1.0@-DIG
	CMPF	TENLST,AC1	;COMPARE TO 1.0
	CFCC			;TRANSFER CONDITIONAL CODES TO CPU
	BGT	RUNE		;IF LESS THAN 1.0@-DIG, PRINT USING CVE
	MOV	WIDTH,R1	;GET THE TOTAL NUMBER OF DIGITS TO BE PRINTED
	ASH	#2,R1		;USE THIS AS A F.P. INDEX
	NEG	R1
	MULF	TENLST(R1),AC1	;CHECK IF GREATER THAN WIDTH-DIG LONG
	CMPF	TENLST,AC1	;COMPARE TO 1.0
	CFCC			;TRANSFER CONDITIONAL CODES
	BGE	RUNF		;IF TOO LARGE, USE CVE
RUNE:	JSR	PC,CVE
	MOV	(SP)+,R1
	RTS	PC
RUNF:	JSR	PC,CVF
	MOV	(SP)+,R1
	RTS	PC


;END OF "CVG"
;"PRTF"  - PRINTING ROUTINE USED BY "CVF", "CVE", & "CVG"

PRTF:	TSTF	AC0		;TEST THE SIGN OF THE NUMBER
	MOVB	#40,MSIGN	;ASSUME SIGN POSITIVE
	CFCC			;TRANSFER THE CONDITIONAL CODES TO CPU
	ABSF	AC0		;CLEAR THE SIGN OF THE NUMBER
	BGE	.+10		
  	MOVB	#55,MSIGN	;IF NEGATIVE PUT IN "-" SIGN
	MODF	TEN,AC0		;COMPUTE M.S. INTEGER DIGIT
	CLR	R3		;INDICATE SIGN NOT YET WRITTEN
DIGLP:	TST	PT		;CHECK IF TIME TO PRINT DECIMAL POINT
	BNE	GETDG		;SKIP IF NOT
	TST	R3		;HAVE WE PRINTED SIGN YET?
	BNE	WTDP		;SKIP IF WE HAVE
	MOVB	MSIGN,(SG)+	;ELSE PRINT SIGN BEFORE DECIMAL POINT
	INC	R3		;INDICATE SIGN PRINTED
WTDP:	MOVB	#56,(SG)+	;PRINT DECIMAL POINT
GETDG:	STCFI	AC1,R2 		;SAVE M.S. INTEGER DIGIT
	CFCC			;CHECK FOR NUMBER TOO LARGE TO INTEGERIZE
	BCC	CHKSZ
TOLGE:	ADDF	AC1,AC0		;IF TWO LARGE, PUT IT BACK TOGETHER
	MODF	TENTH,AC0	;SCALE DOWN AND TRY INTEGERIZING AGAIN
	INC	R1		;PRINT OUT ONE MORE DIGIT
	INC	PT		;SHIFT DECIMAL POINT TO PUT IN EXTRA DIGIT
	TST	R3		;CHECK IF SIGN AND D.P. ALREADY WRITTEN
	BEQ	GETDG		;GO CHECK IF IN RANGE IF NOT WRITTEN
	CLR	R3		;CLEAR SIGN AND D.P.
	SUB	#2,SG		;ADJUST BYTE POINTER
	JMP	GETDG		;GO CHECK IF IN RANGE AGAIN
CHKSZ:	TST     R2              ;TEST INTEGER
	BLT	TOLGE		;IF TOO LARGE, GO SCALE AGAIN
	CMP	R2,#9.		;CHECK IF LESS THAN 9
	BGT	TOLGE		;SCALE IF GREATER THAN 9
      	MODF	TEN,AC0		;START COMPUTING NEXT INTEGER DIGIT
	TST	R3		;HAVE WE PRINTED SIGN YET?
	BNE	SETBS		;SKIP IF WE HAVE
	TST	R2		;CHECK IF LEADING ZERO
	BEQ	WTSP		;IF IT IS GO WRITE A SPACE CHARACTER
	MOVB	MSIGN,(SG)+	;FIRST CHARACTER, NOW PRINT SIGN
	INC	R3		;INDICATE SIGN PRINTED
SETBS:	BIS	#60,R2		;SET ASC ZERO BASE
	JMP	WTCH
WTSP:	MOVB	#40,R2		;WRITE A SPACE CHARACTER
WTCH:	MOVB	R2,(SG)+	;PUT CHARACTER IN I/O BUFFER
	DEC	PT		;DECREMENT DECIMAL POINT COUNT
	SOB	R1,DIGLP	;DONE WITH CHARACTERS?
	RTS	PC		;RETURN


;END OF "PRTF"
;"CVI"&"CVO"   - INTEGER NUMBER TO ASC STRING 

;"CVI"&"CVO" CONVERT THE INTEGER LOADED INTO R0 INTO A ASCII STRING 
;AND APPEND THE NUMBER STRING TO THE STRING POINTED TO BY SG.  SG IS
;LEFT POINTING AT A NULL CHARACTER.  A SAMPLE CALLING SEQUENCE
;FOLLOWS:
;
;		MOV	#NUM,R0		;LOAD NUMBER TO BE CONVERTED
;		MOV	#STRG,SG	;POINT TO OUTPUT STRING
;		JSR	PC,CVI
;
;"CVI" DOES A DECIMAL CONVERSION, WHILE "CVO" WORKS IN BASE 8.

;REGISTERS USED:
;
;	R0, SG PASS ARGUMENTS AND ARE ALTERED

CVI:	MOV	R2,-(SP)	;SAVE REGISTER
	MOV	#10.,R2		;DO A DECIMAL CONVERSION
	BR	CVV

CVO:	MOV	R2,-(SP)	;SAVE REGISTER
	MOV	#8.,R2		;DO A OCTAL CONVERSION

CVV:	MOV	R1,-(SP)	;SAVE REGISTER
	TST	R0		;CHECK SIGN OF NUMBER
	BGE	ITISPL		;BRANCH IF POSITIVE
	NEG	R0		;ELSE COMPLEMENT
	MOVB	#55,(SG)+	;PUSH A "-" INTO STRING
ITISPL:	JSR	PC,DIVLP	;GO FORM STRING RECURSIVELY
	CLRB	(SG) 		;POINT TO A NULL CHARACTER A END OF STG
	MOV	(SP)+,R1	;RESTORE REGISTERS
	MOV	(SP)+,R2
	RTS	PC		;ALL DONE

DIVLP:	MOV	R0,R1		;POSITION NUMBER FOR DIVISION
       	CLR	R0		;CLEAR FOR DIVISION
	DIV	R2,R0		;GET LSD
	ADD	#60,R1		;OR ASC BASE
	MOVB	R1,-(SP)	;SAVE DIGIT
	TST	R0
	BEQ	.+6		;SKIP IF ALL DONE
	JSR	PC,DIVLP	;REPEAT RECURSIVELY
	MOVB	(SP)+,(SG)+	;PUT DIGITS IN STRING
	RTS	PC


;END OF "CVI"
;LOCAL STORAGE AREA

MSIGN:	0		;SIGN OF CURRENT NUMBER
ESIGN:	0		;SIGN OF EXPONENT
EXPON:	0
NUM:	.WORD  0,0
WIDTH:	8. 		;DEFAULT NUMBER OF CHARACTERS IN OUTPUT STRING
DIG:	3		;DEFAULT NUMBER OF DECIMAL DIGITS
OLDW:	8.		;OLD VALUES OF WIDTH AND DIG
OLDD:	3
PT:	0		;NUMBER OF DIGITS BEFORE DECIMAL POINT

;SYSTEM LINE BUFFERS

INBUF:	.BLKW	42.
OUTBUF:	.BLKW	42.

;TABLE OF F.P. DIGITS FROM 0.0 TO 9.0

DGLST:	.WORD        0,     0, 40200,     0, 40400,     0, 40500,     0
	.WORD    40600,     0, 40640,     0, 40700,     0, 40740,     0
	.WORD    41000,     0, 41020,     0

;TABLE OF POWERS OF TEN

	.WORD      531,143735,  1410, 16352,  2252, 22044,  3124,126455
	.WORD     4004,166074,  4646, 23513,  5517,130436,  6401,147263
	.WORD     7242, 41140, 10112,151370, 10775,103666, 11636, 72321
	.WORD    12506, 11006, 13367,113210, 14232,137025, 15101, 66632
	.WORD    15761,144400, 16627, 16640, 17474,162410, 20354, 17112
	.WORD    21223,111356, 22070, 73652, 22746,112625, 23620, 16575
	.WORD    24464, 22334, 25341, 27023, 26214,136314, 27057,165777
	.WORD    27733,163377, 30611, 70137, 31453,146167, 32326,137625
	.WORD    33206, 33675, 34047,142654, 34721,133430, 35603, 11157
	.WORD    36443,153412 
TENTH:	.WORD	 37314,146315 
TENLST:	.WORD	 40200,     0 
TEN:	.WORD	 41040,     0
	.WORD    41710,     0, 42572,     1, 43434, 40000, 44303, 50000
	.WORD    45164, 22001, 46030,113200, 46676,136040, 47556, 65451
	.WORD    50425,  1371, 51272, 41670, 52150,152246, 53021,102347
	.WORD    53665,163041, 54543, 57652, 55416, 15712, 56261,121275
	.WORD    57136,  5554, 60012,143443, 60655, 74354, 61530,153447
	.WORD    62407,103170, 63251, 64027, 64123,141034, 65004, 54522
	.WORD    65645, 67646, 66516,145620, 67401, 37472, 70241,107410
	.WORD    71111,171312, 71774, 67575, 72635,142656, 73505, 33432
	.WORD    74366,102340, 75232, 11414, 76100,113717, 76760,136703
	.WORD    77626, 73232


;END OF "IO" PROGRAM